package com.github.cyf.log;

import com.github.cyf.common.util.IpUtil;
import com.github.cyf.common.util.JacksonUtil;
import com.github.cyf.context.GlobalUserContext;
import com.github.cyf.mapper.UserLogMapper;
import com.github.cyf.pojo.UserLogDO;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;

@Aspect
@Component
public class OperationLogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(OperationLogAspect.class);

    @Autowired
    private UserLogMapper userLogMapper;

    @Pointcut("@annotation(com.github.cyf.log.OperationLog)")
    public void pointcut() {
        // do nothing
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            // 获取方法签名
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            OperationLog logAnnotation = method.getAnnotation(OperationLog.class);
            if (logAnnotation == null) {
                return joinPoint.proceed();
            }
            UserLogDO log = new UserLogDO();
            log.setOperation(logAnnotation.methodDesc());
            // 请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 请求的方法名
            String methodName = signature.getName();
            log.setMethod(className + "." + methodName + "()");
            // 请求的方法参数值
            Object[] args = joinPoint.getArgs();
            // 请求的方法参数名称
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = u.getParameterNames(method);
            if (args != null && paramNames != null) {
                log.setParams(handleParams(args, paramNames));
            }
            log.setGmtCreate(LocalDateTime.now());

            long beginTime = System.currentTimeMillis();
            // 执行方法
            Object result = joinPoint.proceed();
            // 执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            log.setTime(time);

            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            // 设置 IP 地址
            String ip = IpUtil.getIpAddr(request);
            log.setIp(ip);
            // 设置返回值
            log.setResponseText(JacksonUtil.toJsonString(result));
            log.setCreatorId(GlobalUserContext.getUserContext().getUserId());

            userLogMapper.insert(log);
            return result;
        } catch (Exception e) {
            LOGGER.error("保存操作日志发生异常", e);
            throw e;
        }

    }

    /**
     * 参数打印合理化
     *
     * @param args       参数列表
     * @param paramNames 参数名
     * @return
     */
    private String handleParams(Object[] args, Object[] paramNames) {
        Map map = new HashMap();
        for (int i = 0; i < args.length; i++) {
            Object paramName = paramNames[i];
            Object paramVal = args[i];
            if (paramVal instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) paramVal;
                map.put(paramName, file.getOriginalFilename());
            } else {
                map.put(paramName, paramVal);
            }
        }
        return JacksonUtil.toJsonString(map);
    }
}